package sivin.uikit.adapter2

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.collection.SparseArrayCompat
import androidx.viewbinding.ViewBinding

abstract class ItemViewDelegate3<E, Binding : ViewBinding, VH : XViewHolder<Binding>>(
    internal val bindingInflate: (LayoutInflater, ViewGroup, Boolean) -> Binding
) {
    abstract fun onCreateViewHolder(binding: Binding, viewType: Int): XViewHolder<Binding>

    open fun onViewHolderCreate(holder: VH) {}

    abstract fun isForViewType(item: E, position: Int): Boolean

    abstract fun onBindData(holder: VH, item: E, position: Int)

    open fun onViewRecycled(holder: VH, item: E, position: Int) {
    }

    open fun onViewAttachedToWindow(
        holder: XViewHolder<Binding>,
        item: E,
        position: Int
    ) {
    }

    open fun onViewDetachedFromWindow(
        holder: XViewHolder<Binding>,
        item: E,
        position: Int
    ) {
    }
}

abstract class ItemViewDelegate<E, Binding : ViewBinding>(bindingInflate: (LayoutInflater, ViewGroup, Boolean) -> Binding) :
    ItemViewDelegate3<E, Binding, XViewHolder<Binding>>(bindingInflate) {
    override fun onCreateViewHolder(binding: Binding, viewType: Int): XViewHolder<Binding> {
        return XViewHolder(binding, viewType)
    }
}


internal class ItemViewDelegateManager<E, Binding : ViewBinding> {

    private val mDelegates: SparseArrayCompat<ItemViewDelegate3<E, Binding, XViewHolder<Binding>>> by lazy {
        SparseArrayCompat()
    }

    fun addDelegate(delegate: ItemViewDelegate3<E, Binding, XViewHolder<Binding>>) {
        val viewType = mDelegates.size()
        mDelegates.put(viewType, delegate)
    }

    @Suppress("UNUSED")
    fun removeDelegate(delegate: ItemViewDelegate3<E, Binding, XViewHolder<Binding>>) {
        val indexToRemove = mDelegates.indexOfValue(delegate)
        if (indexToRemove >= 0) {
            mDelegates.removeAt(indexToRemove)
        }
    }

    fun getItemViewType(item: E, position: Int): Int {
        val delegatesCount = mDelegates.size()
        for (i in (delegatesCount - 1) downTo 0) {
            val delegate: ItemViewDelegate3<E, Binding, XViewHolder<Binding>> =
                mDelegates.valueAt(i)
            if (delegate.isForViewType(item, position)) {
                return mDelegates.keyAt(i)
            }
        }
        throw IllegalArgumentException(
            "No ItemViewDelegate added that matches position=${position} in data source"
        )
    }

    fun bindData(
        viewType: Int,
        holder: XViewHolder<Binding>,
        item: E,
        position: Int
    ) {
        val delegate = mDelegates[viewType]
        delegate?.onBindData(holder, item, position) ?: run {
            throw IllegalArgumentException(
                "No ItemViewDelegateManager added that matches position=$position in data source"
            )
        }
    }

    fun getItemViewDelegateCount(): Int {
        return mDelegates.size()
    }

    fun getItemViewDelegate(viewType: Int): ItemViewDelegate3<E, Binding, XViewHolder<Binding>> {
        val delegate = mDelegates[viewType]
        delegate?.let {
            return it
        } ?: run {
            throw IllegalArgumentException(
                "No ItemViewDelegateManager added that matches viewType=$viewType in data source"
            )
        }
    }

    fun onRecycleItemView(
        holder: XViewHolder<Binding>,
        item: E,
        position: Int
    ) {
        val delegate = mDelegates[holder.viewType]
        delegate?.onViewRecycled(holder, item, position) ?: run {
            throw IllegalArgumentException(
                "No ItemViewDelegateManager added that matches position=$position in data source"
            )
        }
    }

    fun onViewAttachedToWindow(
        holder: XViewHolder<Binding>,
        item: E,
        position: Int
    ) {
        val delegate = mDelegates[holder.viewType]
        delegate?.onViewAttachedToWindow(holder, item, position) ?: run {
            throw IllegalArgumentException(
                "No ItemViewDelegateManager added that matches position=$position in data source"
            )
        }
    }

    fun onViewDetachedFromWindow(
        holder: XViewHolder<Binding>,
        item: E,
        position: Int
    ) {
        val delegate = mDelegates[holder.viewType]
        delegate?.onViewDetachedFromWindow(holder, item, position) ?: run {
            throw IllegalArgumentException(
                "No ItemViewDelegateManager added that matches position=$position in data source"
            )
        }
    }
}

